package com.lucky.common.config.security;

import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.lucky.common.cache.RightCache;
import com.lucky.common.config.AppConfig;
import com.lucky.common.mapper.PermMapper;
import com.lucky.common.mapper.RoleMapper;
import com.lucky.common.mapper.UserMapper;
import com.lucky.common.pojo.po.UserPO;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
import org.springframework.util.AntPathMatcher;
import org.thymeleaf.extras.springsecurity5.dialect.SpringSecurityDialect;

import java.util.*;
import java.util.function.Supplier;

@Slf4j
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Value("${server.servlet.context-path}")
    String app;

    @Resource
    RightCache rightCache = null;

    @Resource
    PermMapper permMapper;

    @Resource
    UserMapper userMapper = null;

    @Resource
    RoleMapper roleMapper = null;


    private final AntPathMatcher pathMatcher = new AntPathMatcher();

    @Bean
    public SpringSecurityDialect securityDialect() {
        return new SpringSecurityDialect();
    }

    // 配置安全过滤链
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                //匿名用户访问
                .anonymous(AbstractHttpConfigurer::disable)
                // 关闭CSRF保护（实际生产环境根据需要开启）
                .csrf(AbstractHttpConfigurer::disable)
                .cors(AbstractHttpConfigurer::disable)
                .headers(header->header.frameOptions(HeadersConfigurer.FrameOptionsConfig::sameOrigin))

                // 配置授权规则
                .authorizeHttpRequests(authorize -> authorize

                        // 允许访问静态资源和登录页面
                        .antMatchers(AppConfig.ignoreUrls).permitAll()
                        // 管理员页面需要ADMIN角色
                        .antMatchers("/admin/**").hasRole("ADMIN")
                        // 仅要求用户已认证
//                        .anyRequest().authenticated()
                        //匹配url权限, 定制化认证
                        .anyRequest().access(this::checkPermission)
                )

                // 配置表单登录
                .formLogin(form -> form
                        // 自定义登录页面
                        .loginPage("/index")
                        // 登录请求处理路径（与登录表单的action一致）
                        .loginProcessingUrl(app+"/login/login")
                        // 登录成功后的处理
                        .defaultSuccessUrl(app+"/login/main")
                        // 登录失败后跳转的页面
                        .failureUrl(app+"/login/unauthorized")
                        // 允许所有用户访问登录页面
                        .permitAll()
                )

                // 配置退出登录
                .logout(logout -> logout
                        // 退出登录的处理路径
                        .logoutUrl("/login/logout")
                        // 退出登录成功后跳转的页面
                        .logoutSuccessUrl("/index")
                        // 清除认证信息
                        .invalidateHttpSession(true)
                        .permitAll()
                );

        return http.build();
    }


    /**
     * 自定义全局权限校验逻辑
     * @param authentication 当前登录用户的认证信息
     * @param context 请求上下文（包含请求路径、方法等）
     * @return 授权决策（允许/拒绝）
     */
    private AuthorizationDecision checkPermission(Supplier<Authentication> authentication, RequestAuthorizationContext context) {
        boolean hasPermission = false;

        HttpServletRequest request = context.getRequest();
        
        // 1. 获取当前用户和请求信息
        Authentication auth = authentication.get();
        String urlPath = request.getServletPath();
        String requestUri = request.getRequestURI(); // 请求路径
        String httpMethod = request.getMethod();     // 请求方法（GET/POST等）

        LinkedHashMap<String, Object> pathInfo = new LinkedHashMap<>();
        pathInfo.put("工程名", request.getContextPath());
        pathInfo.put("请求路径", request.getServletPath()); // .servlet映射路径
        pathInfo.put("请求URI", request.getRequestURI()); // 完整请求路径（含上下文）
        pathInfo.put("请求URL", request.getRequestURL().toString()); // 完整URL
        pathInfo.put("是否认证：" , auth.isAuthenticated());
        pathInfo.put("用户名：" , auth.getName());
        pathInfo.put("权限：" , auth.getAuthorities());
        pathInfo.forEach((k,v)->log.info("{} = {}", k, v));

        // 2. 校验逻辑1：用户是否已登录（先确保认证）
        if (!auth.isAuthenticated() || auth instanceof AnonymousAuthenticationToken) {
            return new AuthorizationDecision(false); // 未登录，拒绝访问
        }

        // 3. 校验逻辑2：查询用户是否有该路径的权限（动态权限，从数据库获取）
        /*String username = auth.getName();
        List<String> roles = roleMapper.queryRoles(username);
        Set<String> urls = new HashSet<>();
        roles.forEach(role -> urls.addAll(rightCache.roleUrlMap.get(role)));
        hasPermission = urls.contains(urlPath) || urls.contains(urlPath.replaceFirst("/",""));*/

        hasPermission = auth.getAuthorities().stream().anyMatch(a -> a.getAuthority().equals(urlPath));

        // 4. 校验逻辑3：额外业务规则（如IP限制、租户隔离等）
//        String clientIp = context.getRequest().getRemoteAddr();
//        boolean isAllowedIp = "192.168.1.100".equals(clientIp) || "127.0.0.1".equals(clientIp);
//        isAllowedIp = true;
        
        // 5. 综合决策：所有条件满足才允许访问
//        hasPermission = hasPermission && isAllowedIp;
        return new AuthorizationDecision(hasPermission);
    }


}

